/*
  * @file point_list.cpp
  * @author Thibault Merle et Guillaume Poussel
  */
#include <algorithm>
#include <vector>
#include <iterator>

#include "base/point_list.h"
#include "base/sorter.h"
#include "base/Segment.h"

point_list point_list::get_random_points(const unsigned long int nb, const int min, const int max) {
    point_list points;
    for (unsigned long int i = 0; i < nb; ++i) {
        int x = rand()%(max-min)+min;
        int y = rand()%(max-min)+min;
        points.push_back(new Point(x, y));
    }
    return points;
}

void point_list::shuffle() {
    std::vector<Point*> list_as_vector(begin(), end());
    std::random_shuffle(list_as_vector.begin(), list_as_vector.end());
    assign(list_as_vector.begin(), list_as_vector.end());
}

Point* point_list::pick_point() {
    Point* pt = front();
    pop_front();
    return pt;
}

void point_list::move_back(point_list::iterator& current) {
    if (current == begin()) {
        current = end();
    }
    std::advance(current, -1);
}

void point_list::advance(point_list::iterator& current) {
    std::advance(current, 1);
    if (current == end()) {
        current = begin();
    }
}

point_list::iterator point_list::prev_point(point_list::iterator current) {
    move_back(current);
    return current;
}

point_list::iterator point_list::next_point(point_list::iterator current) {
    advance(current);
    return current;
}

Point* point_list::operator[] (const int index) {
    point_list::iterator it = begin();
    std::advance(it, index);
    return *it;
}

Point* point_list::operator[] (const int index) const {
    point_list::const_iterator it = begin();
    std::advance(it, index);
    return *it;
}

Point* point_list::get_centroid() const {
    if (empty()) {
        return new Point(0, 0);
    }
    c_prod_type x_avg = 0, y_avg = 0;

    for (point_list::const_iterator it = begin(); it != end(); ++it) {
        x_avg += (*it)->x();
        y_avg += (*it)->y();
    }
    unsigned list_size = size();
    return new Point((p_type) (x_avg/list_size), (p_type) (y_avg/list_size));
}

point_list point_list::get_diamond() const {
    if (size() <= 4) {
        return *this;
    }
    point_list diamond;
    diamond.push_back(*min_element(begin(), end(), Point_X_Order()));
    diamond.push_back(*min_element(begin(), end(), Point_Y_Order()));
    diamond.push_back(*max_element(begin(), end(), Point_X_Order()));
    diamond.push_back(*max_element(begin(), end(), Point_Y_Order()));
    return diamond;
}


bool point_list::contains(const Point* pt) const {
    // Seul le modulo est important
    bool odd_ints = false;
    for (point_list::const_iterator it = begin(); it != end(); ++it) {
        Point* p1 = *it;
        point_list::const_iterator it_next = it;
        ++it_next;
        Point* p2 = (it_next == end() ? front() : *it_next);
        bool p1_on_radius = p1->is_on_radius(pt);
        bool p2_on_radius = p2->is_on_radius(pt);
        Segment edge(p1, p2);
        if (*p1 == *pt || *p2 == *pt || edge.contains(pt)) {
            return true; // Le point est sur le polygone
        }
        if (p1->y() == p2->y()) {
            // Ne compte pas les intersections avec les arêtes horizontales
        } else if ((p1_on_radius &&
                    p2->y() > pt->y()) ||
                   (p2_on_radius &&
                    p1->y() > pt->y())) {
            odd_ints = !odd_ints;
        } else if (!p1_on_radius && !p2_on_radius) {
            if (edge.intersects_radius(pt)) {
                odd_ints = !odd_ints;
            }
        }
    }
    return odd_ints; // Nombre impair d'intersections = point à l'intérieur
}
